#ifndef __M_ROOM_H__tmp
#define __M_ROOM_H__

#include "online.hpp"
#include "util.hpp"
#include "db.hpp"
#include "logger.hpp"
#include <unordered_map>
#include <memory>

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum { GAME_START, GAME_OVER } room_status;

class room {
private:
    uint64_t _room_id;
    room_status _status;
    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    user_table *_tb_user;
    online_manager *_online_user;
    std::vector<std::vector<int>> _board;
private:
    /*判断是否五星连珠*/
    bool five(int row, int col, int row_offset, int col_offset, int color) {
        int count = 1;
        /*计算当前棋子一侧侧相同棋子个数*/
        int row_searvh = row + row_offset;
        int col_searvh = col + col_offset;
        while(row_searvh >= 0 && row_searvh < BOARD_ROW &&
                col_searvh >= 0 && col_searvh < BOARD_COL &&
                _board[row_searvh][col_searvh] == color) {
            count++;
            row_searvh += row_offset;
            col_searvh += col_offset;
        }
        /*计算当前棋子另一侧相同棋子个数*/
        row_searvh = row - row_offset;
        col_searvh = col - col_offset;
        while(row_searvh >= 0 && row_searvh < BOARD_ROW &&
                col_searvh >= 0 && col_searvh < BOARD_COL &&
                _board[row_searvh][col_searvh] == color) {
            count++;
            row_searvh -= row_offset;
            col_searvh -= col_offset;
        }
        return count >= 5;
    }
    uint64_t check_win(int row, int col, int color) {
        if (five(row, col, 0, 1, color) ||/*横行*/
            five(row, col, 1, 0, color) ||/*竖行*/
            five(row, col, -1, 1, color) ||/*斜行-左斜*/
            five(row, col, 1, 1, color)) {/*反斜行-右斜*/
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }
public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user) 
        : _room_id(room_id),
        _status(GAME_START),
        _player_count(0),
        _tb_user(tb_user),
        _online_user(online_user),
        _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0)){
            DLOG("%lu 房间创建成功!", _room_id);
    }
    ~room(){
        DLOG("%lu 房间销毁成功!", _room_id);
    }
    uint64_t id() { return _room_id; }
    int player_count() { return _player_count; }
    void add_white_user(uint64_t uid) { _white_id = uid; _player_count++; }
    void add_black_user(uint64_t uid) { _black_id = uid; _player_count++; }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }
    /*处理下棋动作*/
    Json::Value handle_chess(Json::Value &req) {
        Json::Value resp = req;
        // 2.判断房间中两个玩家是否都在线，任意一个不在线，则另一方胜利
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_uid = req["uid"].asUInt64();
        if(_online_user->is_in_game_room(_white_id) == false){
            resp["result"] = "true";
            resp["reason"] = "对方掉线，不战而胜!";
            resp["winner"] = (Json::Value::UInt64)_black_id;
            return resp;
        }
        if(_online_user->is_in_game_room(_black_id) == false){
            resp["result"] = "true";
            resp["reason"] = "对方掉线，不战而胜!";
            resp["winner"] = (Json::Value::UInt64)_white_id;
            return resp;
        }
        // 3.获取走棋位置，判断当前走棋是否合理
        if(_board[chess_row][chess_col] != 0){
            resp["result"] = false;
            resp["reason"] = "当前位置有了其他棋子";
            return resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;
        // 4.判断是否有玩家胜利，即从当前位置开始判断是否有五星连珠
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if(winner_id != 0) {
            resp["reason"] = "己方五星连珠，胜利";
        }
        resp["result"] = "true";
        resp["winner"] = (Json::Value::UInt64)winner_id;
        return resp;
    }
    /*处理聊天动作*/
    Json::Value handle_chat(Json::Value &req) {
        Json::Value resp = req;
        // 2.判断是否包含敏感词 - 可以封装为一个专门的接口进行扩展
        std::string msg = req["msg"].asString();
        auto it = msg.find("垃圾");
        if(it != std::string::npos){
            resp["result"] = false;
            resp["reason"] = "信息中含有敏感词!";
            return resp;
        }
        // 3.返回消息
        resp["result"] = true;
        return resp;
    }
    /*处理退出动作*/
    void handle_exit(uint64_t uid) {
        // 1.如果是游戏中退出则对方胜利，如果是游戏结束退出则直接玩家数量--
        Json::Value resp;
        if(_status == GAME_START){
            uint64_t winner_id = (uid == _white_id ? _black_id : _white_id);
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id; 
            resp["optype"] = "put_chess";
            resp["result"] = "true";
            resp["reason"] = "对方掉线，不战而胜!";
            resp["room_id"] = (Json::Value::UInt64)_room_id;
            resp["uid"] = (Json::Value::UInt64)uid;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = (Json::UInt64)winner_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _status = GAME_OVER;
            broadcast(resp);
        }
        _player_count--;
    }
    void handle_request(Json::Value &req) {
        // 1.判断请求的房间号是否与当前房间号匹配
        Json::Value resp;
        uint64_t room_id = req["room_id"].asUInt64();
        if(room_id != _room_id){
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "房间号不匹配!";
            broadcast(resp);
            return;
        }
        
        // 2.根据不同请求类型调用不同处理函数
        std::string req_type = req["optype"].asString();
        if (req_type == "put_chess") {
            resp = handle_chess(req);
            
            if (resp["winner"].asUInt64() != 0) {
                uint64_t winner_id = resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _status = GAME_OVER;
            }
        }
        else if (req_type == "chat") {
            resp = handle_chat(req);
        }
        else {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "请求类型错误!";
        }
        broadcast(resp);
        return;
    }
    /*向房间内所有用户广播消息*/
    void broadcast(Json::Value resp) {
        // 1.对json数据进行序列化
        std::string body;
        json_util::serialize(resp, body);
        // 2.获取房间内所有用户websocket连接 扩展-房间中有观战用户时，获取连接使用数组或链表保存和访问
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
        // 3.依次发送响应信息
        if (wconn != nullptr) {
            wconn->send(body);
        }
        if (bconn != nullptr) {
            bconn->send(body);
        }
    }
};


using room_ptr = std::shared_ptr<room>;

class room_manager {
private:
    uint64_t _next_rid;
    std::mutex _mutex;
    user_table *_tb_user;
    online_manager *_online_user;
    std::unordered_map<uint64_t, room_ptr> _rooms;
    std::unordered_map<uint64_t, uint64_t> _users;

public:
    /*初始化房间id计数器*/
    room_manager(user_table *tb_user, online_manager *online_user) 
        : _tb_user(tb_user),
        _online_user(online_user),
        _next_rid(1) {
            DLOG("房间管理类初始化成功!");
        }
    /*析构函数*/
    ~room_manager() { DLOG("房间管理类销毁成功!"); }
    /*为两个用户创建房间，并返回房间的智能指针对象*/
    room_ptr create_room(uint64_t uid1, uint64_t uid2) {
        // 1.判断两个用户是否已经在游戏大厅中
        if(_online_user->is_in_game_hall(uid1) == false){
            DLOG("用户: %lu 不在游戏大厅!", uid1);
            return room_ptr();
        }
        if(_online_user->is_in_game_hall(uid2) == false){
            DLOG("用户: %lu 不在游戏大厅!", uid2);
            return room_ptr();
        }
        // 2.创建房间，将用户信息添加到房间中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_rid, _tb_user, _online_user));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 3.管理房间信息-建立哈希映射
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;
        // 4.返回房间信息
        return rp;
    }
    /*通过房间id获取房间信息*/
    room_ptr find_room_by_rid(uint64_t rid) {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if(it == _rooms.end()){
            return room_ptr();
        }
        return it->second;
    }
    /*通过用户id获取房间信息*/
    room_ptr find_room_by_uid(uint64_t uid) {
        // 1.加锁
        std::unique_lock<std::mutex> lock(_mutex);
        // 2.通过用户id找到房间id
        auto uit = _users.find(uid);
        if(uit == _users.end()){
            return room_ptr();
        }
        uint64_t rid = uit->second;
        // 3.通过房间id找到房间信息
        auto rit = _rooms.find(rid);
        if(rit == _rooms.end()){
            return room_ptr();
        }
        return rit->second;
    }
    /*通过房间id删除房间*/
    void remove_room_by_rid(uint64_t rid) {
        // 1.通过房间id获取房间信息
        room_ptr rp = find_room_by_rid(rid);
        if(rp == nullptr) {
            return;
        }
        // 2.通过房间信息获取用户id
        uint64_t uid1 = rp->get_white_user();
        uint64_t uid2 = rp->get_black_user();
        // 3.根据用户id删除房间内对应用户信息
        std::unique_lock<std::mutex> lock(_mutex);/*加锁保护*/
        _users.erase(uid1);
        _users.erase(uid2);
        // 4.根据房间id删除房间管理信息
        _rooms.erase(rid);
    }
    /*删除房间中指定用户，如果房间中没有用户了则删除该房间，在用户连接断开时被调用*/
    void remove_room_user(uint64_t uid) {
        //根据用户id获取房间信息
        room_ptr rp = find_room_by_uid(uid);
        if(rp == nullptr) {
            return;
        }
        // 玩家退出操作
        rp->handle_exit(uid);
        // 房间中没有玩家时才销毁房间
        //DLOG("room player count %d", rp->player_count());
        if(rp->player_count() == 0){
            remove_room_by_rid(rp->id());
        }
    }
};

#endif
